#include "TargetDisk.h"
#include "TestSegment.h"
#include <iostream>
#include <time.h>
#include <math.h>
#include "JoinTests.h"
#include <sstream>
#include <string>
#include <fstream>

using namespace std;

void TestingPhysical(int nBytesCount);
void TestingLogical(int nBytesCount);
void TestingRandomPhysical(int nIterationNum, int nByteCount, int nOffsetSectorFactor);
void TestingSequentialPhysical(int nIterationNum, int nByteCount, int nOffsetSectorFactor);
void TestingErasing(int nIterationNum, int nByteCount, int nOffsetSectorFactor);




int main()
{

	if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
   {
      DWORD dwError = GetLastError();
      printf("Failed to enter background mode (%d)\n", dwError);
	  return 0;
   } 

   // Display thread priority

   DWORD dwThreadPri = GetThreadPriority(GetCurrentThread());

   printf("Current thread priority is 0x%x\n", dwThreadPri);

	JoinTests sJoin;

	ofstream fResults ("results.txt");
	float fTimeSec = 0;
	float fHashTime = 0;

	//int anBufferSize[7] = {1024, 10*1024, 10240, 10240, 102400, 102400, 1024000};
	//int anSmallRelSize[7] = {5*1024, 20*1024, 50*1024, 50*1024, 500*1024, 500*1024, 5000*1024};
	//int anBigRelSize[7] = {50*1024, 50*1024, 100*1024, 200*1024, 1000*1024, 5000*1024, 10000*1024};

	// Rel is 5 times the memory size
	//int anBufferSize[8]		= {102400,		1024000,	10240000,	102400000,		102400,		1024000,	10240000,	102400000};
	//int anSmallRelSize[8]	= {400*1024,	4000*1024,	40000*1024, 400000*1024,	500*1024,	5000*1024,	50000*1024, 500000*1024};
	//int anBigRelSize[8]		= {50*1024,		50*1024,	50*1024,	50*1024,		50*1024,	50*1024,	50*1024,	50*1024};

	// Rel is 3 times the memory size
	//int anBufferSize[5]		= {10240,		102400,		1024000,	10240000,	102400000};
	//int anSmallRelSize[5]	= {30*1024,		300*1024,	3000*1024,	30000*1024, 300000*1024};
	//int anBigRelSize[5]		= {50*1024,		50*1024,	50*1024,	50*1024,	50*1024};


	//int anBufferSize[5]		= {10240/2,		102400/2,		1024000/2,	10240000/2,	102400000/2};
	//int anSmallRelSize[5]	= {50*1024,		500*1024,	5000*1024,	50000*1024, 500000*1024};
	//int anBigRelSize[5]		= {50*1024,		50*1024,	50*1024,	50*1024,	50*1024};

	// Rel is 4 times the memory size
	//int anBufferSize[5]		= {25600,		256000,		2560000,		25600000,		256000000};
	//int anSmallRelSize[5]	= {100*1024,	1000*1024,	10000*1024,		100000*1024,	1000000*1024};
	//int anBigRelSize[5]		= {1024,		1024,		1024,			1024,			1024};

	// Rel is twice the memoty size
	//int anBufferSize[5]		= {25600*2,		256000*2,		2560000*2,		25600000*2,		256000000*2};
	//int anSmallRelSize[5]	= {100*1024,	1000*1024,	10000*1024,		100000*1024,		1000000*1024};
	//int anBigRelSize[5]		= {1024,		1024,		1024,			1024,				1024};

	//int anBufferSize[4]		= {10240000,		10240000,		102400000,		10240000};
	//int anSmallRelSize[4]	= {40000*1024,		30000*1024,		200000*1024,	50000*1024};
	//int anBigRelSize[4]		= {512,				512,			512,			512};

	int anBufferSize[8]		= {30720000/2,	307200000/2,	30720000/3,	307200000/3,	30720000/4,	307200000/4,	30720000/5,	307200000/5,};
	int anSmallRelSize[8]	= {30720000,	307200000,		30720000,	307200000,		30720000,	307200000,		30720000,	307200000};
	int anBigRelSize[8]		= {50*1024,		50*1024,		50*1024,	50*1024,		50*1024,	50*1024,		50*1024,	50*1024};


		for (int j = 2; j < 3; ++j)
		{
	for (int i = 6; i < 8; ++i)
	{

		sJoin.nBufferLimitedByteCount = anBufferSize[i];//anBufferSize[i];
		sJoin.nSmallRelByteCount = anSmallRelSize[i];
		sJoin.nBigRelByteCount = anBigRelSize[i];

#ifdef __FLASH__
		sJoin.PreTest(L"\\\\.\\E:\\rel1", L"\\\\.\\E:\\rel2", L"\\\\.\\E:\\res");//, L"\\\\.\\C:\\rel1", L"\\\\.\\C:\\rel2");
#else
		sJoin.PreTest(L"\\\\.\\C:\\rel1", L"\\\\.\\C:\\rel2", L"\\\\.\\C:\\res");
#endif // __FALSH__

		sJoin.PerformTest(fResults, i);

		sJoin.PostTest();
		printf("*************************************************\n");

		Sleep(1000);
		}
	}



	
	return 0;
#ifdef __TEST_LOGICAL__
	TestingLogical(104857600 /*100MB*/);
#else
	///////////////////////////////////////////////////////////////////////

	TestingPhysical(104857600 /*100MB*/);
	TestingErasing(10, 512, 1);

	TestingRandomPhysical(1024, 512 /*1 sector*/, 1);
	TestingSequentialPhysical(1024, 512 /*1 sector*/, 1);

	TestingRandomPhysical(1024, 512 * 32 /*1 erase block*/, 32);
	TestingSequentialPhysical(1024, 512 * 32 /*1 erase block*/, 32);

#endif // __TEST_LOGICAL__
	return 0;
}

void TestingLogical(int nBytesCount)
{
	int nSize = 0;
	char* pszBuffer = NULL;
	CTargetDisk sFlashDisk("\\\\.\\E:\\", eLogical);

	nSize = nBytesCount; //512*128;

	cout << endl;
	cout << "Writing a " << nBytesCount / (pow(2.0, 20)) << " bytes file to the flash stick " << endl;
	cout <<"************************************"<<endl;
	// Insert the test segment to sFlashDisk
	sFlashDisk.m_psTestsList = new CTestSegment(eWrite, nSize, 0);
	sFlashDisk.m_nTestsNum++;
	// Check how to avoid eWrite from declaring here
	bool bRes = sFlashDisk.PreTest("\\\\.\\E:\\test.txt", eWrite);

	if (!bRes)
	{
		return;
	}
	// Inset data to the buffer
	pszBuffer = (char*)sFlashDisk.m_psTestsList->m_pBuffer;
	for (int i = 0; i < nSize; ++i)
	{
		pszBuffer[i] = 'a';
	}

	sFlashDisk.PerformTest();

	sFlashDisk.PostTest();

	cout << endl;
	cout << "Reading a  " << nBytesCount / (pow(2.0, 20)) << "  bytes file from the flash stick " << endl;
	cout <<"************************************"<<endl;
	sFlashDisk.m_psTestsList = new CTestSegment(eRead, nSize, 0);
	sFlashDisk.m_nTestsNum++;
	// Check how to avoid eWrite from declaring here
	sFlashDisk.PreTest("\\\\.\\E:\\test.txt", eRead);

	// Inset data to the buffer
	pszBuffer = (char*)sFlashDisk.m_psTestsList->m_pBuffer;
	for (int i = 0; i < nSize; ++i)
	{
		pszBuffer[i] = 'b';
	}

	sFlashDisk.PerformTest();

	pszBuffer = (char*)sFlashDisk.m_psTestsList->m_pBuffer;
	for (int i = 0; i < nSize; ++i)
	{
		if (pszBuffer[i] != 'a')
		{
			cout << "The reading was not done correctly" << endl;
		}
		break;

	}

	sFlashDisk.PostTest();

}

void TestingPhysical(int nBytesCount)
{
	// Test writing and reading a big amount of data
	int nSize = 0;
	char* pszBuffer = NULL;


	CTargetDisk sFlashPhysDisk("\\\\.\\E:\\", ePhysical);
	cout << "Flash stick is Logical disk " << endl;
	cout <<"************************************"<<endl;
	sFlashPhysDisk.GetLogicalDiskPhysicalInfo();

	nSize = nBytesCount / 4;


	int nMBCount = static_cast<int>(nBytesCount / (pow(2.0,20)));

	cout << endl;
	cout << "Writing " << nMBCount << " MB of raw data to the flash stick " << endl;
	cout <<"*************************************************************************"<<endl;
	// Insert the test segment to sFlashPhysDisk
	sFlashPhysDisk.m_psTestsList = new CTestSegment(eWrite, nSize, 16384);
	sFlashPhysDisk.m_nTestsNum++;
	// Check how to avoid eWrite from declaring here
	sFlashPhysDisk.PreTest("\\\\.\\E:", eWrite);

	// Inset data to the buffer
	pszBuffer = (char*)sFlashPhysDisk.m_psTestsList->m_pBuffer;
	for (int i = 0; i < nSize; ++i)
	{
		pszBuffer[i] = 'b';
	}

	sFlashPhysDisk.PerformTest();

	sFlashPhysDisk.PostTest();

	cout << endl;
	cout << "Reading "<< nMBCount << " MB of raw data from the flash stick " << endl;
	cout <<"*************************************************************************"<<endl;
	sFlashPhysDisk.m_psTestsList = new CTestSegment(eRead, nSize, 16384);
	sFlashPhysDisk.m_nTestsNum++;
	// Check how to avoid eWrite from declaring here
	sFlashPhysDisk.PreTest("\\\\.\\E:", eRead);

	// Inset data to the buffer
	pszBuffer = (char*)sFlashPhysDisk.m_psTestsList->m_pBuffer;
	for (int i = 0; i < nSize; ++i)
	{
		pszBuffer[i] = 'c';
	}

	sFlashPhysDisk.PerformTest();

	pszBuffer = (char*)sFlashPhysDisk.m_psTestsList->m_pBuffer;
	for (int i = 0; i < nSize; ++i)
	{
		if (pszBuffer[i] != 'b')
		{
			cout << "The reading was not done correctly" << endl;
		}
		break;

	}

	sFlashPhysDisk.PostTest();
}

void TestingRandomPhysical(int nIterationNum, int nByteCount, int nOffsetSectorFactor)
{
	cout << endl << "Test writing / reading "<< nIterationNum << " random-access "<< nOffsetSectorFactor << " sectors one after another" << endl;
	cout <<"*************************************************************************"<<endl;

	// Test writing / reading 1024 random-access sectors one after another
	// They will be written in the range of the last test - so all of the sectors will be dirty
	CTargetDisk sFlashPhysDisk("\\\\.\\E:\\", ePhysical);

	// initialize random seed:
	srand ( time(NULL) );

	//int nIterationNum = 1024;
	int* pnRandOffsets = (int*) malloc (nIterationNum * sizeof(int));
	int nRandOffset = 0;
	//int nSize = 512; // 1 sector
	int nSize = nByteCount; // 1 sector
	CTestSegment* psTemp = NULL;

	//////////////////////////////////////////////////////////////////////////
	// Writing 
	bool bIsOk = false;
	for (int i = 0; i < nIterationNum; ++i)
	{
		bIsOk = false;
		while (!bIsOk)
		{
			// We add 32 so the writing won't be on the first sector
			nRandOffset = rand() % nIterationNum + 32;
			for (int j = 0; j <= i; ++ j)
			{
				if ( pnRandOffsets[j] == nRandOffset)
				{
					bIsOk = false;
					break;
				}
				else
				{
					bIsOk = true;
				}
			}	
		}

		psTemp = new CTestSegment(eWrite, nSize, nRandOffset * 512 * nOffsetSectorFactor);
		sFlashPhysDisk.AddTest(psTemp);
		pnRandOffsets[i] = nRandOffset;
	}

	sFlashPhysDisk.PreTest("\\\\.\\E:", eWrite);

	int* pnBuffer = NULL;
	int nNumberOfWords = nSize / 4;
	psTemp = sFlashPhysDisk.m_psTestsList;
	for (int i = 0; i < nIterationNum; ++i)
	{
		pnBuffer = (int*)psTemp->m_pBuffer;
		for (int j = 0; j < nNumberOfWords; ++j)
		{
			pnBuffer[j] = i;
		}	
		psTemp = psTemp->m_psNextSegment;
	}

	sFlashPhysDisk.PerformTest();

	sFlashPhysDisk.PostTest();
	//
	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	// Reading
	for (int i = 0; i < nIterationNum; ++i)
	{
		nRandOffset = pnRandOffsets[i];
		psTemp = new CTestSegment(eRead, nSize, nRandOffset * 512 * nOffsetSectorFactor);
		sFlashPhysDisk.AddTest(psTemp);
	}

	sFlashPhysDisk.PreTest("\\\\.\\E:", eRead);

	psTemp = sFlashPhysDisk.m_psTestsList;
	for (int i = 0; i < nIterationNum; ++i)
	{
		pnBuffer = (int*)psTemp->m_pBuffer;
		for (int j = 0; j < nNumberOfWords; ++j)
		{
			pnBuffer[j] = 0;
		}	
		psTemp = psTemp->m_psNextSegment;
	}

	sFlashPhysDisk.PerformTest();

	psTemp = sFlashPhysDisk.m_psTestsList;
	for (int i = 0; i < nIterationNum; ++i)
	{
		pnBuffer = (int*)psTemp->m_pBuffer;
		for (int j = 0; j < nNumberOfWords; ++j)
		{
			if ( pnBuffer[j] != i)
			{
				cout << i << " - " << pnBuffer[j] << "error: " << endl;
				break;
			}
		}	
		psTemp = psTemp->m_psNextSegment;
	}


	sFlashPhysDisk.PostTest();

}

void TestingSequentialPhysical(int nIterationNum, int nByteCount, int nOffsetSectorFactor)
{
	// Test writing / reading 1024 sequential sectors one after another
	// They will be written in the range of the last test - so all of the sectors will be dirty

	cout << endl << "Test writing / reading "<< nIterationNum << " sequential "<< nOffsetSectorFactor << " sectors one after another" << endl;

	cout <<"*************************************************************************"<<endl;
	CTargetDisk sFlashPhysDisk("\\\\.\\E:\\", ePhysical);

	//int nIterationNum = 1024;
	int* pnRandOffsets = (int*) malloc (nIterationNum * sizeof(int));
	int nRandOffset = 0;
	int nSize = nByteCount; 
	CTestSegment* psTemp = NULL;

	//////////////////////////////////////////////////////////////////////////
	// Writing 
	for (int i = 0; i < nIterationNum; ++i)
	{
		psTemp = new CTestSegment(eWrite, nSize, (i + 32) * 512 * nOffsetSectorFactor);
		sFlashPhysDisk.AddTest(psTemp);
		pnRandOffsets[i] = nRandOffset;
	}

	bool bRes = sFlashPhysDisk.PreTest("\\\\.\\E:", eWrite);
	if (bRes)
	{
		int* pnBuffer = NULL;
		int nNumberOfWords = nSize / 4;
		psTemp = sFlashPhysDisk.m_psTestsList;
		for (int i = 0; i < nIterationNum; ++i)
		{
			pnBuffer = (int*)psTemp->m_pBuffer;
			for (int j = 0; j < nNumberOfWords; ++j)
			{
				pnBuffer[j] = i;
			}	
			psTemp = psTemp->m_psNextSegment;
		}

		sFlashPhysDisk.PerformTest();

		sFlashPhysDisk.PostTest();

	//
	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	// Reading
	for (int i = 0; i < nIterationNum; ++i)
	{
		psTemp = new CTestSegment(eRead, nSize, (i + 32) * 512 * nOffsetSectorFactor);
		sFlashPhysDisk.AddTest(psTemp);
	}

	sFlashPhysDisk.PreTest("\\\\.\\E:", eRead);

	psTemp = sFlashPhysDisk.m_psTestsList;
	for (int i = 0; i < nIterationNum; ++i)
	{
		pnBuffer = (int*)psTemp->m_pBuffer;
		for (int j = 0; j < nNumberOfWords; ++j)
		{
			pnBuffer[j] = 0;
		}	
		psTemp = psTemp->m_psNextSegment;
	}

	sFlashPhysDisk.PerformTest();

	psTemp = sFlashPhysDisk.m_psTestsList;
	for (int i = 0; i < nIterationNum; ++i)
	{
		pnBuffer = (int*)psTemp->m_pBuffer;
		for (int j = 0; j < nNumberOfWords; ++j)
		{
			if ( pnBuffer[j] != i)
			{
				cout << i << " - " << pnBuffer[j] << "error: " << endl;
				break;
			}
		}	
		psTemp = psTemp->m_psNextSegment;
	}


	sFlashPhysDisk.PostTest();
	}
	
}

void TestingErasing(int nIterationNum, int nByteCount, int nOffsetSectorFactor)
{
	// Write to the first sector of an erase block and then write to the other 31 sectors of
	// the same erase block and compare the times
	// Do it a lot of times in order to get a reliable mean

	cout << endl<< " Testing Erasing "<< nIterationNum << " times. " << endl;
	cout <<"*************************************************************************"<<endl;
	CTargetDisk sFlashPhysDisk("\\\\.\\E:\\", ePhysical);

	double* pdFirstWriteMean = (double*) malloc (nIterationNum * sizeof(double));
	double* pdOthersWriteMean = (double*) malloc (nIterationNum * sizeof(double));

	int nSize = nByteCount; 
	CTestSegment* psTemp = NULL;
	int i = 0;
	bool bRes = true;
	int* pnBuffer = NULL;
	int nNumberOfWords = nSize / 4;
	DWORD dwTime = 0;

	//////////////////////////////////////////////////////////////////////////
	// Writing 
	for (i = 0; i < nIterationNum; ++i)
	{
		psTemp = new CTestSegment(eWrite, nSize, (i + 32) * 512 * nOffsetSectorFactor);
		sFlashPhysDisk.AddTest(psTemp);
		bRes = sFlashPhysDisk.PreTest("\\\\.\\E:", eWrite);
		if (bRes)
		{
			pnBuffer = (int*)psTemp->m_pBuffer;
			for (int j = 0; j < nNumberOfWords; ++j)
			{
				pnBuffer[j] = i;
			}	
		}
		pdFirstWriteMean[i] = sFlashPhysDisk.PerformTest();
		sFlashPhysDisk.PostTest();


		for (int j = 1; j < 32; ++j)
		{
			psTemp = new CTestSegment(eWrite, nSize, (i + 32 + j) * 512 * nOffsetSectorFactor);
			sFlashPhysDisk.AddTest(psTemp);
		}


		bRes = sFlashPhysDisk.PreTest("\\\\.\\E:", eWrite);

		for (int j = 1; j < 32; ++j)
		{
			if (bRes)
			{
				pnBuffer = (int*)psTemp->m_pBuffer;
				for (int k = 0; k < nNumberOfWords; ++k)
				{
					pnBuffer[k] = j;
				}	
			}
		}

		pdOthersWriteMean[i] = sFlashPhysDisk.PerformTest();
		sFlashPhysDisk.PostTest();
	}

	double dFirstWriting = 0.0;
	double dOtherWriting = 0.0;
	for (i = 0;  i < nIterationNum; ++i)
	{
		dFirstWriting += pdFirstWriteMean[i];
		dOtherWriting += pdOthersWriteMean[i];
	}

	cout << endl << endl << "It took " << dFirstWriting / nIterationNum << " sec to write to the first sector" << endl << endl;
	cout << endl << endl << "It took " << dOtherWriting / nIterationNum << " sec to write to the next 31 sectors" << endl;
	cout << "Means that it took " << dOtherWriting / (nIterationNum * 31.0) << " sec to write every other sector." << endl;



}
